/*
 * Copyright (c) 2009-2010 Microelectronic Center, 
 * Harbin Institute of Technology
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gou Pengfei
 *
 * Date: Aug. 2010
 */

#include "cpu/edge/edge_sim_point.hh"
#include "base/loader/symtab.hh"
#include "cpu/edge/atomic/atomic_block.hh"

template<class Impl>
EdgeSimPoint<Impl>::EdgeSimPoint(std::string &start_sym, std::string &end_sym, 
        int call_num, int ret_num, std::string &mode)
    :_startFunctionName(start_sym), 
    _endFunctionName(end_sym), 
    _startPointAddr(0),
    _endPointAddrMin(0),
    _endPointAddrMax(0),
    _mode(mode), 
    _callNum(-1),
    _retNum(-1),
    dynNum(-1),
    retNumBeforeStart(-1)
{
    _callNum = call_num;

    if (!debugSymbolTable->findAddress(start_sym, _startPointAddr)) {
        panic("Can't find valid address for simpoint symbols.\n");
    }

    DPRINTF(EdgeSimPoint, "Set start simpoint to %s@%#x.\n", 
            start_sym, _startPointAddr);

    _retNum = ret_num;

    // Find the minimum block address for this function
    if (!debugSymbolTable->findAddress(end_sym, _endPointAddrMin)) {
        panic("Can't find valid address for simpoint symbols.\n");
    }

    // Then find the maximum block address for this function
    std::string temp_func_name;

    // With respect to TRIPS convention, all the blocks belonging to a
    // specific function will named as follows: 'func_name',
    // 'func_name$1', 'func_name$2' ...
    //temp_func_name = end_sym + '$';

    _endPointAddrMax = _endPointAddrMin;

    for (int i = 1; ; i++) {

        Addr temp_addr;

        temp_func_name = end_sym + '$';
        temp_func_name += csprintf("%d", i);

        if (!debugSymbolTable->findAddress(temp_func_name, temp_addr)) {
            break;
        }

        _endPointAddrMax = temp_addr;

        assert(_endPointAddrMax > _endPointAddrMin);
    }

    DPRINTF(EdgeSimPoint, "Set end simpoint to %s@%#x to %#x.\n", 
            end_sym, _endPointAddrMin, _endPointAddrMax);

    if (mode == "StartPoint") {

        // Initialize this variable to be prepared for recording the number of rets
        // before the start point.
        retNumBeforeStart = 0;
        dynNum = 0;

        DPRINTF(EdgeSimPoint, "This is a start simpoint.\n");
    } else if (mode == "EndPoint") {
        // End point needs to know how many rets have occured before the
        // restored start point.
        // Here dynNum will be initialized to be non-negative when this point
        // is unserialized.

        DPRINTF(EdgeSimPoint, "This is an end simpoint.\n");
    } else {
        panic("Unrecogonized simpoint mode.\n");
    }

}

template<class Impl>
EdgeSimPoint<Impl>::EdgeSimPoint()
    : _startPointAddr(0),
    _endPointAddrMin(0),
    _endPointAddrMax(0),
    _mode("Invalid"),
    _callNum(-1), 
    _retNum(-1),
    dynNum(-1),
    retNumBeforeStart(-1)
{
    // Leave functionName to be blank.
}

template<class Impl>
EdgeSimPoint<Impl>::~EdgeSimPoint()
{
}

template<class Impl>
bool
EdgeSimPoint<Impl>::checkStartPoint(EdgeBlockPtr &edge_block)
{
    assert(edge_block->isCompletionCondSatisfied());
    assert(_mode == "StartPoint");
    assert(retNumBeforeStart >= 0 && dynNum >= 0);

    TheISA::ExitType exit_type = edge_block->getExitType();

    if (exit_type == TheISA::call) {

        Addr call_addr = edge_block->getBranchTarget();

        //DPRINTF(EdgeSimPoint, "Checking call for addr: %#x \n",
        //        call_addr);

        if (call_addr == _startPointAddr) {
            dynNum++;
            DPRINTF(EdgeSimPoint, "Addr @%#x matched."
                    " dynNum = %d.\n", call_addr, dynNum);
        }

        if (dynNum == _callNum) {
            return true;
        }
    } else if (exit_type == TheISA::ret) {

        Addr ret_addr = edge_block->getStartPC();

        //DPRINTF(EdgeSimPoint, "Checking ret from addr: %#x \n",
        //        ret_addr);

        if (ret_addr >= _endPointAddrMin && 
                ret_addr <= _endPointAddrMax) {

            retNumBeforeStart++;

            DPRINTF(EdgeSimPoint, "Addr @%#x is a ret before start point."
                    " retNumBeforeStart = %d.\n", ret_addr, retNumBeforeStart);
        }
    }

    return false;
}

template<class Impl>
bool
EdgeSimPoint<Impl>::checkEndPoint(EdgeBlockPtr &edge_block)
{
    assert(edge_block->isCompletionCondSatisfied());
    assert(_mode == "EndPoint");
    assert(retNumBeforeStart >= 0 && dynNum >= 0);

    if (edge_block->getExitType() != TheISA::ret) {
        return false;
    }

    Addr ret_addr = edge_block->getStartPC();

    DPRINTF(EdgeSimPoint, "Checking ret from addr: %#x \n",
            ret_addr);

    if (ret_addr >= _endPointAddrMin && 
            ret_addr <= _endPointAddrMax) {

        dynNum++;

        DPRINTF(EdgeSimPoint, "Addr @%#x matched."
                " dynNum = %d.\n", ret_addr, dynNum);
    }

    if (dynNum == _retNum) {
        return true;
    }

    return false;
}

template<class Impl>
void
EdgeSimPoint<Impl>::serialize(std::ostream &os)
{
    // We can only serialize the simpoint in START POINT mode.
    // The only variable here to serialize is the number of rets before start
    // point and this variable should not be negative if someone intends to
    // serialize it.
    assert(_mode == "StartPoint" && retNumBeforeStart >= 0);
    SERIALIZE_SCALAR(retNumBeforeStart);
}

template<class Impl>
void
EdgeSimPoint<Impl>::unserialize(Checkpoint *cp, const std::string &section)
{
    // We can only unserialize in ENDPOINT mode.
    // The variable to be unserialized from the checkpoint is
    // retNumBeforeStart so that we can calculate the correct number of
    // rets to a specific end point.
    assert(_mode == "EndPoint");
    UNSERIALIZE_SCALAR(retNumBeforeStart);

    // Set the dynNum to be retNumBeforeStart thus we can get the initial
    // value of the number of rets.
    assert(retNumBeforeStart >= 0);
    dynNum = retNumBeforeStart;
}

//template class EdgeSimPoint<AtomicEdgeCPUImpl>;
//template class EdgeSimPoint<EdgeCPUImpl>;

